home *** CD-ROM | disk | FTP | other *** search
/ AGA Toolkit '97 / The AGA Toolkit '97.iso / miscellaneous / science / maths / calc / source / file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-07  |  10.7 KB  |  551 lines

  1. /*
  2.  * Copyright (c) 1994 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * File I/O routines callable by users.
  7.  */
  8.  
  9. #include "stdarg.h"
  10. #include "calc.h"
  11.  
  12.  
  13. #define    READSIZE    1024    /* buffer size for reading */
  14.  
  15. /*
  16.  * Definition of opened files.
  17.  */
  18. typedef struct {
  19.     FILEID id;        /* id to identify this file */
  20.     FILE *fp;        /* real file structure for I/O */
  21.     char *name;        /* file name */
  22.     BOOL reading;        /* TRUE if opened for reading */
  23.     BOOL writing;        /* TRUE if opened for writing */
  24.     char *mode;        /* open mode */
  25. } FILEIO;
  26.  
  27.  
  28. /*
  29.  * Table of opened files.
  30.  * The first three entries always correspond to stdin, stdout, and stderr,
  31.  * and cannot be closed.  Their file ids are always 0, 1, and 2.
  32.  */
  33. static FILEIO files[MAXFILES] = {
  34.     FILEID_STDIN,  NULL,  "(stdin)",  TRUE, FALSE, "reading",
  35.     FILEID_STDOUT, NULL, "(stdout)", FALSE, TRUE, "writing",
  36.     FILEID_STDERR, NULL, "(stderr)", FALSE, TRUE, "writing"
  37. };
  38.  
  39. static FILEID lastid = FILEID_STDERR;        /* last allocated file id */
  40.  
  41.  
  42. /*
  43.  * file_init - perform needed initilization work
  44.  *
  45.  * On some systems, one cannot initialize a pointer to a FILE *.
  46.  * This routine, called once at startup is a work-a-round for
  47.  * systems with such bogons.
  48.  */
  49. void
  50. file_init()
  51. {
  52.     static int done = 0;    /* 1 => routine already called */
  53.  
  54.     if (!done) {
  55.     files[0].fp = stdin;
  56.     files[1].fp = stdout;
  57.     files[2].fp = stderr;
  58.     done = 1;
  59.     }
  60. }
  61.  
  62.  
  63. /*
  64.  * Open the specified file name for reading or writing as determined by
  65.  * the specified mode ("r", "w", or "a").  Returns a file id which can be
  66.  * used to do I/O to the file, or else FILEID_NONE if the open failed.
  67.  * Aborts with an error if too many files are opened or the mode is illegal.
  68.  */
  69. FILEID
  70. openid(name, mode)
  71.     char *name;        /* file name */
  72.     char *mode;        /* open mode */
  73. {
  74.     FILEIO *fiop;        /* file structure */
  75.     FILEID id;        /* new file id */
  76.     int count;
  77.  
  78.     if (((*mode != 'r') && (*mode != 'w') && (*mode != 'a')) || mode[1])
  79.         math_error("Illegal mode for fopen");
  80.  
  81.     count = MAXFILES;
  82.     do {
  83.         if (--count < 0)
  84.             math_error("Too many open files");
  85.         id = ++lastid;
  86.         fiop = &files[id % MAXFILES];
  87.  
  88.     } while (fiop->reading || fiop->writing);
  89.  
  90.     fiop->name = (char *)malloc(strlen(name) + 1);
  91.     if (fiop->name == NULL) {
  92.         lastid--;
  93.         math_error("No memory for filename");
  94.     }
  95.     strcpy(fiop->name, name);
  96.  
  97.     fiop->fp = fopen(name, mode);
  98.     if (fiop->fp == NULL) {
  99.         free(fiop->name);
  100.         fiop->name = NULL;
  101.         lastid--;
  102.         return FILEID_NONE;
  103.     }
  104.  
  105.     switch (*mode) {
  106.         case 'r':
  107.             fiop->mode = "reading";
  108.             fiop->reading = TRUE;
  109.             break;
  110.         case 'w':
  111.             fiop->mode = "writing";
  112.             fiop->writing = TRUE;
  113.             break;
  114.         case 'a':
  115.             fiop->mode = "appending";
  116.             fiop->writing = TRUE;
  117.             break;
  118.     }
  119.  
  120.     fiop->id = id;
  121.  
  122.     return id;
  123. }
  124.  
  125.  
  126. /*
  127.  * Find the file I/O structure for the specified file id, and verify that
  128.  * it is opened in the required manner ('r' for reading or 'w' for writing).
  129.  * If mode is 0, then no open checks are made at all, and NULL is then
  130.  * returned if the id represents a closed file.
  131.  */
  132. static FILEIO *
  133. findid(id, mode)
  134.     int mode;
  135.     FILEID id;
  136. {
  137.     FILEIO *fiop;        /* file structure */
  138.     static char *msg;
  139.     BOOL flag = 0;
  140.  
  141.     if ((id < 0) || (id > lastid))
  142.         math_error("Illegal file id");
  143.  
  144.     fiop = &files[id % MAXFILES];
  145.  
  146.     switch (mode) {
  147.         case 'r':
  148.             msg = "Reading from";
  149.             flag = fiop->reading;
  150.             break;
  151.         case 'w':
  152.             msg = "Writing to";
  153.             flag = fiop->writing;
  154.             break;
  155.         case 0:
  156.             msg = NULL;
  157.             break;
  158.         default:
  159.             math_error("Unknown findid mode");
  160.     }
  161.  
  162.     if (fiop->id != id) {
  163.         if (msg)
  164.             math_error("%s closed file", msg);
  165.         return NULL;
  166.     }
  167.  
  168.     if (msg && !flag)
  169.         math_error("%s file not opened that way", msg);
  170.     
  171.     return fiop;
  172. }
  173.  
  174.  
  175. /*
  176.  * Return whether or not a file id is valid.  This is used for if tests.
  177.  */
  178. BOOL
  179. validid(id)
  180.     FILEID id;
  181. {
  182.     return (findid(id, 0) != NULL);
  183. }
  184.  
  185.  
  186. /*
  187.  * Return the file id for the entry in the file table at the specified index.
  188.  * Returns FILEID_NONE if the index is illegal or the file is closed.
  189.  */
  190. FILEID
  191. indexid(index)
  192.     long index;
  193. {
  194.     FILEIO *fiop;        /* file structure */
  195.  
  196.     if ((index < 0) || (index >= MAXFILES))
  197.         return FILEID_NONE;
  198.  
  199.     fiop = &files[index];
  200.     if (fiop->reading || fiop->writing)
  201.         return fiop->id;
  202.  
  203.     return FILEID_NONE;
  204. }
  205.  
  206.  
  207. /*
  208.  * Close the specified file id.  Returns TRUE if there was an error.
  209.  * Closing of stdin, stdout, or stderr is illegal, but closing of already
  210.  * closed files is allowed.
  211.  */
  212. BOOL
  213. closeid(id)
  214.     FILEID id;
  215. {
  216.     FILEIO *fiop;        /* file structure */
  217.     int err;
  218.  
  219.     if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
  220.         (id == FILEID_STDERR))
  221.             math_error("Cannot close stdin, stdout, or stderr");
  222.  
  223.     fiop = findid(id, 0);
  224.     if (fiop == NULL)
  225.         return FALSE;
  226.  
  227.     fiop->id = FILEID_NONE;
  228.     if (!fiop->reading && !fiop->writing)
  229.         math_error("Closing non-opened file");
  230.     fiop->reading = FALSE;
  231.     fiop->writing = FALSE;
  232.  
  233.     if (fiop->name)
  234.         free(fiop->name);
  235.     fiop->name = NULL;
  236.  
  237.     err = ferror(fiop->fp);
  238.     err |= fclose(fiop->fp);
  239.     fiop->fp = NULL;
  240.  
  241.     return (err != 0);
  242. }
  243.  
  244.  
  245. /*
  246.  * Return whether or not an error occurred to a file.
  247.  */
  248. BOOL
  249. errorid(id)
  250.     FILEID id;
  251. {
  252.     FILEIO *fiop;        /* file structure */
  253.  
  254.     fiop = findid(id, 0);
  255.     if (fiop == NULL)
  256.         math_error("Closed file for ferror");
  257.     return (ferror(fiop->fp) != 0);
  258. }
  259.  
  260.  
  261. /*
  262.  * Return whether or not end of file occurred to a file.
  263.  */
  264. BOOL
  265. eofid(id)
  266.     FILEID id;
  267. {
  268.     FILEIO *fiop;        /* file structure */
  269.  
  270.     fiop = findid(id, 0);
  271.     if (fiop == NULL)
  272.         math_error("Closed file for feof");
  273.     return (feof(fiop->fp) != 0);
  274. }
  275.  
  276.  
  277. /*
  278.  * Flush output to an opened file.
  279.  */
  280. void
  281. flushid(id)
  282.     FILEID id;
  283. {
  284.     FILEIO *fiop;        /* file structure */
  285.  
  286.     fiop = findid(id, 'w');
  287.     fflush(fiop->fp);
  288. }
  289.  
  290.  
  291. /*
  292.  * Read the next line from an opened file.
  293.  * Returns a pointer to an allocated string holding the null-terminated
  294.  * line (without any terminating newline), or else a NULL pointer on an
  295.  * end of file or error.
  296.  */
  297. void
  298. readid(id, retptr)
  299.     FILEID    id;        /* file to read from */
  300.     char **retptr;        /* returned pointer to string */
  301. {
  302.     FILEIO *fiop;        /* file structure */
  303.     char *str;        /* current string */
  304.     int len;        /* current length of string */
  305.     int totlen;        /* total length of string */
  306.     char buf[READSIZE];    /* temporary buffer */
  307.  
  308.     totlen = 0;
  309.     str = NULL;
  310.  
  311.     fiop = findid(id, 'r');
  312.  
  313.     while (fgets(buf, READSIZE, fiop->fp) && buf[0]) {
  314.         len = strlen(buf);
  315.         if (totlen)
  316.             str = (char *)realloc(str, totlen + len + 1);
  317.         else
  318.             str = (char *)malloc(len + 1);
  319.         if (str == NULL)
  320.             math_error("No memory in freadline");
  321.         strcpy(&str[totlen], buf);
  322.         totlen += len;
  323.         if (buf[len - 1] == '\n') {
  324.             str[totlen - 1] = '\0';
  325.             *retptr = str;
  326.             return;
  327.         }
  328.     }
  329.     if (totlen && ferror(fiop->fp)) {
  330.         free(str);
  331.         str = NULL;
  332.     }
  333.     *retptr = str;
  334. }
  335.  
  336.  
  337. /*
  338.  * Return the next character from an opened file.
  339.  * Returns EOF if there was an error or end of file.
  340.  */
  341. int
  342. getcharid(id)
  343.     FILEID id;
  344. {
  345.     return fgetc(findid(id, 'r')->fp);
  346. }
  347.  
  348.  
  349. /*
  350.  * Print out the name of an opened file.
  351.  * If the file has been closed, a null name is printed.
  352.  * If flags contain PRINT_UNAMBIG then extra information is printed
  353.  * identifying the output as a file and some data about it.
  354.  */
  355. void
  356. printid(id, flags)
  357.     int flags;
  358.     FILEID id;
  359. {
  360.     FILEIO *fiop;        /* file structure */
  361.     FILE *fp;
  362.  
  363.     fiop = findid(id, 0);
  364.     if (fiop == NULL) {
  365.         math_str((flags & PRINT_UNAMBIG) ? "FILE (closed)" : "\"\"");
  366.         return;
  367.     }
  368.     if ((flags & PRINT_UNAMBIG) == 0) {
  369.         math_chr('"');
  370.         math_str(fiop->name);
  371.         math_chr('"');
  372.         return;
  373.     }
  374.  
  375.     fp = fiop->fp;
  376.     math_fmt("FILE \"%s\" (%s, pos %ld", fiop->name,  fiop->mode,
  377.         ftell(fp));
  378.     if (ferror(fp))
  379.         math_str(", error");
  380.     if (feof(fp))
  381.         math_str(", eof");
  382.     math_chr(')');
  383. }
  384.  
  385.  
  386. /*
  387.  * Print a formatted string similar to printf.  Various formats of output
  388.  * are possible, depending on the format string AND the actual types of the
  389.  * values.  Mismatches do not cause errors, instead something reasonable is
  390.  * printed instead.  The output goes to the file with the specified id.
  391.  */
  392. void
  393. idprintf(id, fmt, count, vals)
  394.     int count;
  395.     FILEID id;            /* file id to print to */
  396.     char *fmt;            /* standard format string */
  397.     VALUE **vals;            /* table of values to print */
  398. {
  399.     FILEIO *fiop;
  400.     VALUE *vp;
  401.     char *str;
  402.     int ch, len;
  403.     int oldmode, newmode;
  404.     long olddigits, newdigits;
  405.     long width, precision;
  406.     BOOL didneg, didprecision;
  407.  
  408.     fiop = findid(id, 'w');
  409.  
  410.     math_setfp(fiop->fp);
  411.  
  412.     while ((ch = *fmt++) != '\0') {
  413.         if (ch == '\\') {
  414.             ch = *fmt++;
  415.             switch (ch) {
  416.                 case 'n': ch = '\n'; break;
  417.                 case 'r': ch = '\r'; break;
  418.                 case 't': ch = '\t'; break;
  419.                 case 'f': ch = '\f'; break;
  420.                 case 'v': ch = '\v'; break;
  421.                 case 'b': ch = '\b'; break;
  422.                 case 0:
  423.                     math_setfp(stdout);
  424.                     return;
  425.             }
  426.             math_chr(ch);
  427.             continue;
  428.         }
  429.  
  430.         if (ch != '%') {
  431.             math_chr(ch);
  432.             continue;
  433.         }
  434.  
  435.         /*
  436.          * Here to handle formats.
  437.          */
  438.         didneg = FALSE;
  439.         didprecision = FALSE;
  440.         width = 0;
  441.         precision = 0;
  442.  
  443.         ch = *fmt++;
  444.         if (ch == '-') {
  445.             didneg = TRUE;
  446.             ch = *fmt++;
  447.         }
  448.         while ((ch >= '0') && (ch <= '9')) {
  449.             width = width * 10 + (ch - '0');
  450.             ch = *fmt++;
  451.         }
  452.         if (ch == '.') {
  453.             didprecision = TRUE;
  454.             ch = *fmt++;
  455.             while ((ch >= '0') && (ch <= '9')) {
  456.                 precision = precision * 10 + (ch - '0');
  457.                 ch = *fmt++;
  458.             }
  459.         }
  460.         if (ch == 'l')
  461.             ch = *fmt++;
  462.  
  463.         oldmode = _outmode_;
  464.         newmode = oldmode;
  465.         olddigits = _outdigits_;
  466.         newdigits = olddigits;
  467.         if (didprecision)
  468.             newdigits = precision;
  469.  
  470.         switch (ch) {
  471.             case 'd':
  472.             case 's':
  473.             case 'c':
  474.                 break;
  475.             case 'f':
  476.                 newmode = MODE_REAL;
  477.                 break;
  478.             case 'e':
  479.                 newmode = MODE_EXP;
  480.                 break;
  481.             case 'r':
  482.                 newmode = MODE_FRAC;
  483.                 break;
  484.             case 'o':
  485.                 newmode = MODE_OCTAL;
  486.                 break;
  487.             case 'x':
  488.                 newmode = MODE_HEX;
  489.                 break;
  490.             case 'b':
  491.                 newmode = MODE_BINARY;
  492.                 break;
  493.             case 0:
  494.                 math_setfp(stdout);
  495.                 return;
  496.             default:
  497.                 math_chr(ch);
  498.                 continue;
  499.         }
  500.  
  501.         if (--count < 0)
  502.             math_error("Not enough arguments for fprintf");
  503.         vp = *vals++;
  504.  
  505.         math_setdigits(newdigits);
  506.         math_setmode(newmode);
  507.  
  508.         /*
  509.          * If there is no width specification, or if the type of
  510.          * value requires multiple lines, then just output the
  511.          * value directly.
  512.          */
  513.         if ((width == 0) ||
  514.             (vp->v_type == V_MAT) || (vp->v_type == V_LIST))
  515.         {
  516.             printvalue(vp, PRINT_NORMAL);
  517.             math_setmode(oldmode);
  518.             math_setdigits(olddigits);
  519.             continue;
  520.         }
  521.  
  522.         /*
  523.          * There is a field width.  Collect the output in a string,
  524.          * print it padded appropriately with spaces, and free it.
  525.          * However, if the output contains a newline, then ignore
  526.          * the field width.
  527.          */
  528.         math_divertio();
  529.         printvalue(vp, PRINT_NORMAL);
  530.         str = math_getdivertedio();
  531.         if (strchr(str, '\n'))
  532.             width = 0;
  533.         len = strlen(str);
  534.         while (!didneg && (width > len)) {
  535.             width--;
  536.             math_chr(' ');
  537.         }
  538.         math_str(str);
  539.         free(str);
  540.         while (didneg && (width > len)) {
  541.             width--;
  542.             math_chr(' ');
  543.         }
  544.         math_setmode(oldmode);
  545.         math_setdigits(olddigits);
  546.     }
  547.     math_setfp(stdout);
  548. }
  549.  
  550. /* END CODE */
  551.